home *** CD-ROM | disk | FTP | other *** search
/ Eagles Nest BBS 8 / Eagles_Nest_Mac_Collection_Disc_8.TOAST / Developer Environments / AllegroCL11 / Examples / Cosell.Lisp < prev    next >
Encoding:
Text File  |  1987-10-27  |  32.2 KB  |  941 lines  |  [TEXT/CCL ]

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;Cosell, a Common Lisp spread-sheet
  3. ;;
  4. ;;copyright 1987, Coral Software Corp
  5. ;;
  6. ;;  Cosell just like a normal spread-sheet, except that you enter Lisp
  7. ;;  expressions the cells.  It is written in Allegro CL version 1.1.
  8. ;;
  9. ;;  To run Cosell, just evaluate this file.  The code will add a new menu
  10. ;;  which provides the Cosell functionality.  In addition, the Save, Save As…,
  11. ;;  and Close menu-items from the File menu can be used, and also Cut, Copy,
  12. ;;  Paste, and Clear from the Edit menu.
  13. ;;
  14. ;;  Each cell in a Cosell window can contain a Lisp expression.  A cell can
  15. ;;  can access the value of another cell through an absolute or relative
  16. ;;  reference.  The expression (C 5 3) would return the value of cell #@(5 5).
  17. ;;  The expression (R 5 3) would return the value of the cell 5 rows to the
  18. ;;  right and 3 rows down from the cell containing the expression.  Relative
  19. ;;  references may contain negative numbers.
  20. ;;
  21. ;;  Cosell calculates the value of cells using a depth-first, demand-driven
  22. ;;  evaluation.  It starts in the upper left corner, and begins calculating
  23. ;;  the values of of cell.  If a cell contains a forward reference to another
  24. ;;  cell, the value of the other cell is calculated.  This may in turn trigger
  25. ;;  the calculation of other cells.  Cells are marked when they are calculated,
  26. ;;  so each calculation occurs only once during a sweep through a spreadsheet.
  27. ;;
  28. ;;  The only restriction on the action performed by cells is that they cannot
  29. ;;  side-effect the value of another cell.  Allowing this would introduce
  30. ;;  order dependencies and make forward references impossible.
  31. ;;
  32. ;;
  33. ;;  The object variable declarations are only used to suppress compiler
  34. ;;  warnings.  They don't improve the speed of the code.
  35. ;;
  36.  
  37. (eval-when (eval compile)
  38.   (require 'records)
  39.   (require 'traps))
  40.  
  41.  
  42. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  43. ;;
  44. ;;A few little utilities.
  45.  
  46.  
  47. ;;;;;;;;;
  48. ;;
  49. ;;*40-space-string*
  50. ;;
  51. ;;  this is used for formatting in the trace feature
  52. ;;
  53. (defvar *40-space-string* "                                        ")
  54.  
  55. ;;;;;;;;;
  56. ;;
  57. ;;point-to-list
  58. ;;
  59. ;;  converts a point to a list of h-coordinate and v-coordinate
  60. ;;
  61. (defun point-to-list (point)
  62.   (list (point-h point) (point-v point)))
  63.  
  64. ;;;;;;;;;
  65. ;;
  66. ;;draw-cell
  67. ;;
  68. ;;  an extension to table-dialog-items.
  69. ;;  this function used to redraw a single cell
  70. ;;
  71. ;;  looks much prettier than redrawing the entire table.
  72. ;;
  73. (defobfun (draw-cell *table-dialog-item*) (cell)
  74.   (declare (object-variable my-dialog wptr))
  75.   (let* ((cell-pos (cell-position cell)))    
  76.     (when cell-pos
  77.       (with-port (ask my-dialog wptr)
  78.         (rlet ((cell-rect rect))
  79.           (rset cell-rect rect.topleft cell-pos)
  80.           (rset cell-rect rect.bottomright (add-points cell-pos
  81.                                                        (cell-size)))
  82.           (ccl::draw-table-cell cell
  83.                                 cell-rect
  84.                                 (cell-selected-p cell)))))))
  85.  
  86. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  87. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  88. ;;
  89. ;;  The Cosell window and it's components
  90. ;;
  91. ;;
  92.  
  93. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  94. ;;
  95. ;;*cosell-table*
  96. ;;
  97. ;;  Every Cosell window contains a cosell table.
  98. ;;  The Cosell table is what actually displays the spreadsheet
  99. ;;
  100. ;;  Because the Macintosh List Manager is used, large tables don't perform
  101. ;;  wonderfully.  This file creates spreadsheets with seven by 10 cells
  102. ;;  (though more can certainly be done, it will take longer to create
  103. ;;  a window).
  104. ;;
  105.  
  106. (defobject *cosell-table* *array-dialog-item*)
  107.  
  108. (defobfun (exist *cosell-table*) (init-list)
  109.   (usual-exist init-list)
  110.   (have 'evaled-cell-array                   ;cells in this array are non-nil
  111.         (make-array (array-dimensions        ;when the corresponding cell in
  112.                      (table-array))))        ;the table has been evaluated.
  113.   (have 'undo-cell #@(0 0))                  ;the last cell which was changed
  114.   (have 'undo-text "")                       ;the old text of the last changed
  115.                                              ;  cell
  116.   (have 'table-trace-p nil)                  ;true of trace is turned on for
  117.                                              ;  the table
  118.   (have 'trace-indent 0)                     ;the current depth of the trace
  119.   (have 'current-cell-list ())               ;a stack of cells whose evaluation
  120.                                              ;  is pending.
  121. ;these last three are initialized to their proper values by the exist
  122. ;  procedure of the tables owning window.
  123.   (have 'entry-text nil)                     ;editable text for entering
  124.                                              ;  formulas
  125.   (have 'auto-calc-p nil))                   ;true if the spreadsheet should
  126.                                              ;  recalculate automatically
  127.                                              ;  every time a cell is changed
  128.  
  129.  
  130. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  131. ;;
  132. ;;Accessor functions for getting and setting the values of cells
  133. ;;
  134. ;;  Each cell potentially has three components:
  135. ;;     1.  The value (result) from the last time the cell was calculated.
  136. ;;     2.  A compiled expression to be funcalled whenever their is a need
  137. ;;         to recalculate the value of the cell.
  138. ;;     3.  The text of the expression entered by the user.
  139. ;;
  140. ;;  These three components are stored in a list of length 3.
  141. ;;
  142. ;;  If the contents of a cell is NIL, it means that it hasn't been given
  143. ;;  an expression, so it never needs to be recalculated.
  144. ;;
  145.  
  146.  
  147. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  148.   ;;
  149.   ;;  Accessors
  150.   ;;
  151. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  152.  
  153.  
  154. ;;;;;;;;;;;;;;
  155. ;;
  156. ;;full-cell-contents
  157. ;;
  158. ;;  returns the entire contents of the cell.  This will either be NIL, or
  159. ;;  a list of three components.
  160. ;;
  161. (defobfun (full-cell-contents *cosell-table*) (cell &aux subscript)
  162.   (if (setq subscript (cell-to-subscript cell))
  163.     (apply #'aref (table-array) subscript)))
  164.  
  165. ;;;;;;;;;;;;;;
  166. ;;
  167. ;;result-cell-contents
  168. ;;
  169. ;;  returns the result from the last time the cell was calculated, or NIL
  170. ;;    if the cell has no contents
  171. ;;
  172. (defobfun (result-cell-contents *cosell-table*) (cell)
  173.   "first item in the list is the calculated value"
  174.    (first (full-cell-contents cell)))
  175.  
  176. ;;;;;;;;;;;;;;
  177. ;;
  178. ;;action-cell-contents
  179. ;;
  180. ;;  returns a function to be funcalled to get the value of the cell, or NIL
  181. ;;    if the cell has no contents.
  182. ;;
  183. (defobfun (action-cell-contents *cosell-table*) (cell)
  184.   "second item in the list is the compiled definition"
  185.   (second (full-cell-contents cell)))
  186.  
  187. ;;;;;;;;;;;;;;
  188. ;;
  189. ;;text-cell-contents
  190. ;;
  191. ;;  returns the text of the formula entered into the cell by the user, or
  192. ;;    an empty string if the cell is empty.
  193. ;;
  194. (defobfun (text-cell-contents *cosell-table*) (cell)
  195.   "third item in the list is the text of the body of the definition"
  196.   (or
  197.    (third (full-cell-contents cell))
  198.    ""))
  199.  
  200. ;;;;;;;;;;;;;;
  201. ;;
  202. ;;cell-contents
  203. ;;
  204. ;;  This function is used by the system calls which print the contents of
  205. ;;    the cell of a table.  We set it up so that the result-cell-contents
  206. ;;    are printed.
  207. ;;
  208. (defobfun (cell-contents *cosell-table*) (cell)
  209.   "this function is called to print the cell"
  210.   (result-cell-contents cell))
  211.  
  212.  
  213. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  214.   ;;
  215.   ;;  Modifiers
  216.   ;;
  217. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  218.  
  219.  
  220. ;;;;;;;;;;;;;;
  221. ;;
  222. ;;set-full-cell-contents
  223. ;;
  224. ;;  Sets the contents of the cell.  The new-contents argument should be
  225. ;;    a list of three components, or NIL.
  226. ;;
  227. (defobfun (set-full-cell-contents *cosell-table*) (cell new-contents)
  228.   (setf
  229.      (apply #'aref (table-array) (cell-to-subscript cell))
  230.      new-contents)
  231.   (draw-cell cell))
  232.  
  233. ;;;;;;;;;;;;;;
  234. ;;
  235. ;;set-result-contents
  236. ;;
  237. ;;  sets the result part of the cell, and redraws the cell.  This will be
  238. ;;   the value returned by funcalling the cell's formula, or it will be the
  239. ;;   the text of the formula if calculation hasn't happened yet.
  240. ;;
  241. (defobfun (set-result-contents *cosell-table*) (cell new-contents)
  242.   (let ((old-full-contents (full-cell-contents cell)))
  243.     (setf (first old-full-contents) new-contents))
  244.   (draw-cell cell))
  245.  
  246. ;;;;;;;;;;;;;;
  247. ;;
  248. ;;set-action-contents
  249. ;;
  250. ;;  sets the action part of the cell.  The new-contents argument should be
  251. ;;  a compiled function, which is funcalled to get the value of the cell.
  252. ;;
  253. (defobfun (set-action-contents *cosell-table*) (cell new-contents)
  254.   (let ((old-full-contents (full-cell-contents cell)))
  255.     (setf (second old-full-contents) new-contents)))
  256.  
  257. ;;;;;;;;;;;;;;
  258. ;;
  259. ;;set-text-contents
  260. ;;
  261. ;;  sets the text part of the cell.  The new-contents argument should be the
  262. ;;  the text of the formula which was entered by the user.
  263. ;;
  264. (defobfun (set-text-contents *cosell-table*) (cell new-contents)
  265.   (let ((old-full-contents (full-cell-contents cell)))
  266.     (setf (third old-full-contents) new-contents)))
  267.  
  268. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  269.   ;;
  270.   ;;  Mungers
  271.   ;;     procedures which manipulate the contents of cells
  272.   ;;
  273. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  274.  
  275.  
  276. ;;;;;;;;;;;;;;
  277. ;;
  278. ;;update-value
  279. ;;
  280. ;;  calculates the value of the cell.
  281. ;;
  282. ;;  this involves printing trace information and various other hair.
  283. ;;
  284. ;;  The cell will only be calculated if it hasn't already been calculated this
  285. ;;  time around.  It can tell, because if it _has_ been calculated, then the
  286. ;;  corresponding position in evaled-cell-arry will be non-nil.
  287. ;;
  288. ;;  Of course, no calculation occurs if the cell is empty.
  289. ;;
  290. (defobfun (update-value *cosell-table*) (cell)
  291.   (declare (object-variable evaled-cell-array table-trace-p
  292.                             trace-indent current-cell-list))
  293.   (let* ((the-fun (action-cell-contents cell))
  294.          (h-dim (point-h cell))
  295.          (v-dim (point-v cell))
  296.          (cell-string (point-string cell)))
  297.     (when                                    ;recalculate when
  298.       (and the-fun                           ;  there is a function
  299.            (not                              ;  and the cell hasn't been
  300.             (aref evaled-cell-array h-dim v-dim)))     ;  calculated yet.
  301.       (when table-trace-p                    ;print trace info if necessary
  302.         (format t "~%~aStarting cell ~a."
  303.                 (subseq *40-space-string* 0 (min trace-indent 40))
  304.                 cell-string)
  305.         (incf trace-indent))
  306.       (when                                  ;if the cell is already on the
  307.                                              ;  of cells pending calculation
  308.                                              ;  it means we're circular, so
  309.                                              ;  we just punt.
  310.         (member cell current-cell-list :test #'eq)
  311.         (setq current-cell-list ())          ;zero pending cells first
  312.         (error "circularity detected in cell ~a.  Recalculation aborted."
  313.                cell-string))
  314.       (push cell current-cell-list)          ;mark the cell as pending
  315.       (set-result-contents cell              ;recalc the cell
  316.                            (funcall the-fun))
  317.       (setf                                  ;note that the cell is calculated
  318.        (aref evaled-cell-array h-dim v-dim) t)
  319.       (pop current-cell-list)                ;remove it from the pending list
  320.       (when table-trace-p                    ;print trace info if necessary
  321.         (decf trace-indent)
  322.         (format t "~%~aFinished cell ~a."
  323.                 (subseq *40-space-string* 0 (min trace-indent 40))
  324.                 cell-string)))))
  325.  
  326. ;;;;;;;;;;;;;;
  327. ;;
  328. ;;new-contents-from-text
  329. ;;
  330. ;;  given text newly entered by the user, it resets the full contents of the
  331. ;;  cell.
  332. ;;
  333. ;;  The value is set to the text with a prepended asterix (this is immediately
  334. ;;      recalculated if auto-calculation is turned on).
  335. ;;  The action is set to a function compiled from the text
  336. ;;  The text is just the text
  337. ;;
  338. (defobfun (new-contents-from-text *cosell-table*) (cell text)
  339.   (if (equal text "")
  340.     (set-full-cell-contents cell nil)
  341.     (let* ((new-function (progn
  342.                            (compile nil
  343.                                     `(lambda ()
  344.                                          ,(read-from-string text nil nil)))))
  345.            (new-value (concatenate 'string "*" text)))
  346.       (set-full-cell-contents cell
  347.                               (list new-value new-function text)))))
  348.  
  349. ;;;;;;;;;;;;;;
  350. ;;
  351. ;;change-cell
  352. ;;
  353. ;;  moves from one cell to another, depending on the character typed by the
  354. ;;  user.  The character is passed to change-cell as an argument.
  355. ;;
  356. ;;  The old-cell is unhighlighted, and the new cell is highlighted.
  357. ;;  In addition, the text of the new cell is displayed in the text area.
  358. ;;
  359. ;;  The four arrow-keys move right, left, up, and down.
  360. ;;  Tab moves to the right and shift-tab left.
  361. ;;  Return moves down and shift-return moves up.
  362. ;;
  363. (defobfun (change-cell *cosell-table*) (cell char)
  364.   "shifts the selected cell according to the typed character, and updates
  365.    the display.  Does not perform any recalculation."
  366.   (declare (object-variable entry-text))
  367.   (let* ((next-cell (case char
  368.                       (#\downarrow
  369.                        (add-points cell #@(0 1)))
  370.                       (#\forwardarrow
  371.                        (add-points cell #@(1 0)))
  372.                       (#\uparrow
  373.                        (subtract-points cell #@(0 1)))
  374.                       (#\backarrow
  375.                        (subtract-points cell #@(1 0)))
  376.                       (#\tab
  377.                        (if (shift-key-p)
  378.                            (subtract-points cell #@(1 0))
  379.                            (add-points cell #@(1 0))))
  380.                       (#\return
  381.                        (if (shift-key-p)
  382.                            (subtract-points cell #@(0 1))
  383.                            (add-points cell #@(0 1)))))))
  384.     (when (cell-to-subscript next-cell)
  385.       (cell-deselect cell)
  386.       (cell-select next-cell)
  387.       (let* ((next-text (text-cell-contents next-cell)))
  388.         (ask entry-text
  389.           (set-dialog-item-text next-text)
  390.           (dialog-item-draw))))))
  391.  
  392.  
  393. ;;;;;;;;;;;;;;
  394. ;;
  395. ;;dialog-item-action
  396. ;;
  397. ;;  This function is sometimes called when the user clicks in a cell in the
  398. ;;  table.  It is called by window-click-event-handler
  399. ;;
  400. ;;  window-click-event-handler _won't_ call this function if the user is in
  401. ;;  the midst of entering a formula.  In those cases, window-click-event-handler
  402. ;;  uses the click to insert a cell reference into the formula.
  403. ;;
  404. ;;  On single clicks, it selects the cell and displays the cell's text in the
  405. ;;  the text area.
  406. ;;
  407. ;;  On double clicks, it prints the cell's value to the listener.  This is
  408. ;;  useful for cell's whose value has a long print representation.
  409. ;; 
  410. ;;
  411. (defobfun (dialog-item-action *cosell-table*) ()
  412.   (declare (object-variable entry-text))
  413.   (let* ((the-cell (car (selected-cells)))
  414.          (the-text (text-cell-contents the-cell)))
  415.     (if (double-click-p)
  416.       (print (result-cell-contents the-cell))
  417.       (ask entry-text
  418.         (set-dialog-item-text the-text)))))
  419.  
  420.  
  421. ;;;;;;;;;;;;;;
  422. ;;
  423. ;;calculate
  424. ;;
  425. ;;  recalculates the entire spreadsheet.
  426. ;;
  427. ;;  first it zeroes the array, indicating that no cells have been calculated.
  428. ;;  then it goes through and calculates the values of each cell row by row
  429. ;;  and column by column.  Note that the calculation of some cells will by
  430. ;;  forced in advance.  Each cell is calculated as soon as its value is needed
  431. ;;  by another cell.  This way forward references work.  Marking calculated
  432. ;;  cells in the evaled-cell-array insures that no cell is calculated twice.
  433. ;;
  434. ;;  this function returns T.
  435. ;;
  436. (defobfun (calculate *cosell-table*) ()
  437.   (declare (object-variable evaled-cell-array table-trace-p trace-indent))
  438.   (let ((dim (array-dimensions (table-array))))
  439.     (dotimes (column (car dim))
  440.       (dotimes (row (cadr dim))
  441.         (setf (aref evaled-cell-array column row) nil)))
  442.     (when table-trace-p
  443.       (terpri)
  444.       (setq trace-indent 0))
  445.     (dotimes (column (car dim))
  446.       (dotimes (row (cadr dim))
  447.         (update-value (make-point column row)))))
  448.   t)
  449.  
  450. ;;;;;;;;;;;;;;
  451. ;;
  452. ;;c
  453. ;;
  454. ;;  this function takes two arguments, vertical and horizontal coordinates
  455. ;;  of a cell.  It forces the evaluation of the cell (the cell will
  456. ;;  recalculate only if it has to), and returns the cell's value.
  457. ;;
  458. ;;  the name of this function is intentionally brief, so that it can fit
  459. ;;  comfortably inside formulas.
  460. ;;
  461. (defobfun (c *cosell-table*) (h v &aux (cell (make-point h v)))
  462.   "for referring to other cells with absolute coordinates"
  463.   (update-value cell)
  464.   (result-cell-contents cell))
  465.  
  466. ;;;;;;;;;;;;;;
  467. ;;
  468. ;;r
  469. ;;
  470. ;;  this function is analogous to c, except the numbers it receives as
  471. ;;  arguments are taken as relative offsets from the cell containing
  472. ;;  the formula.  These offsets may be positive or negative.
  473. ;;
  474. ;;  The function knows which cell's formula contains it, because that cell
  475. ;;  will be on the top of the stack of cells pending evaluation.
  476. ;;
  477. (defobfun (r *cosell-table*) (h v)
  478.   "for referring to cells with relative coordinates"
  479.   (declare (object-variable current-cell-list))
  480.   (let* ((cell (add-points (car current-cell-list)
  481.                            (make-point h v))))
  482.     (update-value cell)
  483.     (result-cell-contents cell)))
  484.   
  485.  
  486. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  487. ;;
  488. ;;*cosell-window*
  489. ;;
  490. ;;  The spreadsheet window class.
  491. ;;
  492.  
  493. (defobject *cosell-window* *dialog*)
  494.  
  495. ;;;;;;;;;;;;;;
  496. ;;
  497. ;;exist
  498. ;;
  499. ;;  First create a window, and then add items to it.
  500. ;;
  501. ;;  Because we shadow window-update to look at the state of the window, we
  502. ;;  have to make sure window-update doesn't get called before the window
  503. ;;  has the proper state (i.e. instance variables and items).  This means
  504. ;;  we create the window hidden, set it up, and then do a window-show.
  505. ;;
  506. ;;  Setting up involves adding two dialog-items and then setting the state of
  507. ;;  some instance variables.
  508. ;;
  509. (defobfun (exist *cosell-window*) (init-list)
  510.   (declare (object-variable entry-text spread-table
  511.                             table-trace-p auto-calc-p))
  512.   (setq init-list
  513.         (init-list-default init-list
  514.                            :window-type :document-with-zoom
  515.                            :window-show nil
  516.                            :window-font '("monaco" 9)
  517.                            :window-size #@(492 185)
  518.                            :window-position #@(6 40)
  519.                            :window-title "Untitled Spreadsheet"))
  520.   (usual-exist init-list)
  521.   (add-dialog-items
  522.    (have 'entry-text
  523.          (oneof *editable-text-dialog-item*
  524.                 :dialog-item-position #@(2 2)
  525.                 :dialog-item-size #@(10 10)  ;size will be reset
  526.                 :allow-returns t))
  527.    (have 'spread-table
  528.          (oneof *cosell-table*
  529.                 :dialog-item-position #@(0 56)
  530.                 :dialog-item-size #@(10 10)  ;size will be reset
  531.                 :table-array (make-array
  532.                               (point-to-list
  533.                                (getf init-list :table-dimensions #@(7 10)))))))
  534.   (let* ((temp entry-text))
  535.     (ask spread-table
  536.       (setq entry-text temp
  537.             table-trace-p (getf init-list :table-trace-p nil)
  538.             auto-calc-p (getf init-list :auto-calc-p t))))
  539.   (have 'previous-size (window-size))
  540.   (have 'text-changed-p nil)
  541.   (have 'my-file-name nil)
  542.   (ask spread-table (cell-select #@(0 0)))
  543.   (window-size-items)
  544.   (window-show))
  545.  
  546. ;;;;;;;;;;;;;;
  547. ;;
  548. ;;cut, paste, and clear
  549. ;;
  550. ;;  these need to do the usual version, and then set a flag showing that the
  551. ;;  window has been changed.
  552. ;;
  553. (defobfun (cut *cosell-window*) ()
  554.   (declare (object-variable text-changed-p))
  555.   (setq text-changed-p t)
  556.   (usual-cut))
  557.  
  558. (defobfun (paste *cosell-window*) ()
  559.   (declare (object-variable text-changed-p))
  560.   (setq text-changed-p t)
  561.   (usual-paste))
  562.  
  563. (defobfun (clear *cosell-window*) ()
  564.   (declare (object-variable text-changed-p))
  565.   (setq text-changed-p t)
  566.   (usual-clear))
  567.  
  568.  
  569. ;;;;;;;;;;;;;;
  570. ;;
  571. ;;set-window-size
  572. ;;
  573. ;;  Does a usual-set-window-size and then resizes the items in the window. 
  574. ;;
  575. (defobfun (set-window-size *cosell-window*) (new-size)
  576.   (without-interrupts
  577.    (usual-set-window-size new-size)
  578.    (window-size-items)))
  579.  
  580.  
  581. ;;;;;;;;;;;;;;
  582. ;;
  583. ;;window-zoom-event-handler
  584. ;;
  585. ;;  Does a usual-set-window-zoom-event-handler and then resizes the items
  586. ;;  in the window. 
  587. ;;
  588. (defobfun (window-zoom-event-handler *cosell-window*) (message)
  589.   (without-interrupts
  590.    (usual-window-zoom-event-handler message)
  591.    (window-size-items)))
  592.  
  593. ;;;;;;;;;;;;;;
  594. ;;
  595. ;;window-size-items
  596. ;;
  597. ;;  This is called when the window is resized or zoomed.
  598. ;;  First it resizes the dialog items in the window to fit the new size of
  599. ;;  the window.
  600. ;;  Then it invalidates the entire contents of the window to force redrawing
  601. ;;  of the whole thing.
  602. ;;  All this is done before the window is redrawn.
  603. ;;
  604. (defobfun (window-size-items *cosell-window*) ()
  605.   (declare (object-variable spread-table entry-text wptr))
  606.   (without-interrupts
  607.    (let ((new-size (window-size)))
  608.      (ask spread-table
  609.        (set-dialog-item-size (subtract-points new-size
  610.                                               #@(0 56))))
  611.      (ask entry-text
  612.        (set-dialog-item-size (make-point (- (point-h new-size)
  613.                                             4)
  614.                                          49))))
  615.    (when (window-shown-p)
  616.      (with-port wptr
  617.        (_invalrect :ptr (rref wptr window.portrect))))))
  618.  
  619. ;;;;;;;;;;;;;;
  620. ;;
  621. ;;window-click-event-handler
  622. ;;
  623. ;;  This does one of two things:
  624. ;;
  625. ;;  If the click was in a cell, and if the user was currently editing the
  626. ;;  formula of a cell, then a reference to the clicked cell is inserted into
  627. ;;  the formula.
  628. ;;
  629. ;;  If the click wasn't in a cell, or if a formula wasn't being edited, then
  630. ;;  the usual click is done.  This lets the user edit the editable-text, scroll
  631. ;;  the table, or move between cells. 
  632. ;;
  633. (defobfun (window-click-event-handler *cosell-window*) (where)
  634.   (declare (object-variable spread-table text-changed-p))
  635.   (let ((cell-clicked (ask spread-table (point-to-cell where))))
  636.     (unless (and text-changed-p
  637.                  cell-clicked
  638.                  (insert-clicked-cell cell-clicked))
  639.       (usual-window-click-event-handler where))))
  640.  
  641. ;;;;;;;;;;;;;;
  642. ;;
  643. ;;insert-clicked-cell
  644. ;;
  645. ;;  Inserts a reference to a cell into the formula currently being edited.
  646. ;;  It receives the cell as an argument.
  647. ;;
  648. ;;  This always inserts an absolute cell reference, but it could be changed to
  649. ;;  insert an absolute or a relative reference, depending on the value of a
  650. ;;  global or object variable, or on the state of various modifier keys.
  651. ;;
  652. (defobfun (insert-clicked-cell *cosell-window*) (cell)
  653.   (push (concatenate 'string " (c "
  654.                      (subseq (point-string cell) 3))
  655.         *killed-strings*)
  656.   (paste)
  657.   (pop *killed-strings*))
  658.  
  659.  
  660. ;;;;;;;;;;;;;;
  661. ;;
  662. ;;window-key-event-handler
  663. ;;
  664. ;;  window-key-event-handler usually just passes keystrokes to the editable
  665. ;;  text item.  However, it first filters for the keys which are used to
  666. ;;  move from one cell to another.  These are the arrow keys, return, and tab.
  667. ;;
  668. ;;  If it gets one of these special keys, it does several things:
  669. ;;     If the formula has been changed, it sets up undo, gets the new
  670. ;;     formula, changes the cell's contents, and recalculates if auto-calc-p
  671. ;;     is true.
  672. ;;     In any case, it calls change-cell to move to a new cell.
  673. ;;
  674. ;;  To insert a carriage return into a formula, hold down the option key when
  675. ;;  typing return.
  676. ;;
  677. (defobfun (window-key-event-handler *cosell-window*) (char)
  678.   (declare (object-variable spread-table text-changed-p
  679.                             undo-cell undo-text entry-text auto-calc-p))
  680.   (let* ((current-cell (ask spread-table
  681.                          (car (selected-cells)))))
  682.     (if (and (member char
  683.                      '(#\return #\tab #\backarrow
  684.                        #\forwardarrow #\uparrow #\downarrow)
  685.                      :test #'eq)
  686.              (not (option-key-p)))
  687.       (progn
  688.         (when text-changed-p
  689.           (setq text-changed-p nil)
  690.           (ask spread-table
  691.             (setq undo-cell current-cell)
  692.             (setq undo-text (text-cell-contents current-cell))
  693.             (let* ((new-text (ask entry-text (dialog-item-text))))
  694.               (new-contents-from-text current-cell new-text)
  695.               (if auto-calc-p
  696.                 (calculate)))))
  697.         (ask spread-table
  698.           (change-cell current-cell char)))
  699.       (progn
  700.         (setq text-changed-p t)
  701.         (usual-window-key-event-handler char)))))
  702.  
  703. ;;;;;;;;;;;;;;
  704. ;;
  705. ;;undo
  706. ;;
  707. ;;  this function is called from the undo menu-item.  When a cell has been
  708. ;;  changed, it lets the user revert to the old version of the cell.
  709. ;;
  710. ;;  Once the user starts entering a new formula, undo is disabled.
  711. ;;
  712. (defobfun (undo *cosell-window*) ()
  713.   (declare (object-variable spread-table undo-cell undo-text
  714.                             entry-text auto-calc-p))
  715.   (ask spread-table
  716.     (let* ((now-selected (car (selected-cells)))
  717.            (removed-text (text-cell-contents undo-cell))
  718.            (added-text undo-text))
  719.       (cell-deselect now-selected)
  720.       (cell-select undo-cell)
  721.       (new-contents-from-text undo-cell undo-text)
  722.       (ask entry-text
  723.         (set-dialog-item-text added-text))
  724.       (setq undo-text removed-text)         ;sets up for redo
  725.       (if auto-calc-p
  726.         (calculate)
  727.         (draw-cell undo-cell)))))
  728.  
  729. ;;;;;;;;;;;;;;
  730. ;;
  731. ;;window-can-undo-p
  732. ;;
  733. ;;  determines whether the undo menu-item is enabled, and sets the text of the
  734. ;;  undo menu-item.
  735. ;;
  736. ;;  the item will be enabled when the user has just changed a cell, but hasn't
  737. ;;  yet started editing a new formula.
  738. ;;
  739. (defobfun (window-can-undo-p *cosell-window*) ()
  740.   (declare (object-variable text-changed-p))
  741.   (let* ((enabled text-changed-p))
  742.     (ask *undo-menu-item*
  743.       (if enabled
  744.         (progn
  745.           (set-menu-item-title "Undo")
  746.           ())
  747.         (set-menu-item-title "Undo Cell Change")))))
  748.  
  749. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  750. ;;
  751. ;;  A set of functions for saving and reading in spreadsheets
  752. ;;
  753.  
  754.  
  755. ;;;;;;;;;;;;;;
  756. ;;
  757. ;;spread-to-list
  758. ;;
  759. ;;  converts the contents of a spreadsheet to a list.  This list can then be
  760. ;;  printed or stored in a file.
  761. ;;  The list includes the size of the spreadsheet (because they can actually
  762. ;;  be made with varying numbers of cells), whether it traces and/or
  763. ;;  autocalculates, and it stores the text of the formulas in each cell.
  764. ;;
  765. (defobfun (spread-to-list *cosell-window*) ()
  766.   (declare (object-variable spread-table table-trace-p auto-calc-p))
  767.   (ask spread-table
  768.     (let* ((the-list ())
  769.            (the-array (table-array))
  770.            (dimensions (array-dimensions the-array)))
  771.       (dotimes (h (car dimensions))
  772.         (dotimes (v (cadr dimensions))
  773.           (push (text-cell-contents (make-point h v))
  774.                 the-list)))
  775.       (setq the-list (nreverse the-list))
  776.       (push table-trace-p the-list)
  777.       (push auto-calc-p the-list)
  778.       (push dimensions the-list))))
  779.  
  780.  
  781. ;;;;;;;;;;;;;;
  782. ;;
  783. ;;list-to-spread
  784. ;;
  785. ;;  Does the inverse of spread-to-list.  It creates a new spreadsheet window
  786. ;;  from a list of data.
  787. ;;
  788. ;;  This function is used by the open command.
  789. ;;
  790. (defun list-to-spread (the-list)
  791.   (declare (object-variable spread-table auto-calc-p))
  792.   (let* ((dimensions (pop the-list))
  793.          (h (car dimensions))
  794.          (v (cadr dimensions))
  795.          (new-spread (oneof *cosell-window*
  796.                             :table-dimensions (make-point h v)
  797.                             :auto-calc-p (pop the-list)
  798.                             :table-trace-p (pop the-list))))
  799.     (ask new-spread
  800.       (ask spread-table
  801.         (dotimes (hn h)
  802.           (dotimes (vn v)
  803.             (new-contents-from-text (make-point hn vn) (pop the-list))))
  804.         (when auto-calc-p
  805.           (calculate))))
  806.     new-spread))
  807.  
  808.  
  809. ;;;;;;;;;;;;;;
  810. ;;
  811. ;;window-save
  812. ;;
  813. ;;  This function is called by the standard Save menu-item from the File
  814. ;;  menu.  If the window has a file, it stores the spreadsheet in the file.
  815. ;;  If the window doesn't have a file, it calls window-save-as.
  816. ;;
  817. (defobfun (window-save *cosell-window*) ()
  818.   (declare (object-variable my-file-name))
  819.   (if my-file-name
  820.     (with-open-file (the-file my-file-name
  821.                               :direction :output
  822.                               :if-exists :supersede)
  823.       (print (spread-to-list) the-file))
  824.     (window-save-as)))
  825.  
  826. ;;;;;;;;;;;;;;
  827. ;;
  828. ;;window-save-as
  829. ;;
  830. ;;  This function is called by the standard Save As… menu item.  It prompts the
  831. ;;  user for the name of a new file, stores the spreadsheet in the window, and
  832. ;;  sets the window's filename and title appropriately.
  833. ;;
  834. (defobfun (window-save-as *cosell-window*) ()
  835.   (declare (object-variable my-file-name))
  836.   (let* ((new-name (choose-new-file-dialog
  837.                     :prompt "Save Spreadsheet As…"
  838.                     :directory (or my-file-name
  839.                                    "Untitled Spreadsheet"))))
  840.     (with-open-file (the-file new-name
  841.                               :direction :output
  842.                               :if-exists :supersede)
  843.       (print (spread-to-list) the-file))
  844.     (setq my-file-name new-name)
  845.     (set-window-title (mac-filename new-name))))
  846.  
  847.  
  848. ;;;;;;;;;;;;;;
  849. ;;
  850. ;;cosell-open
  851. ;;
  852. ;;  this command is called from the Open menu-item on the Cosell menu.  It
  853. ;;  opens a text file, and creates a spreadsheet from the list read in from
  854. ;;  the file.
  855. ;;
  856. ;;  This function will let you open any text file.  If you choose a file that
  857. ;;  doesn't hold a cosell spreadsheet, it will lose.
  858. ;;
  859. (defun cosell-open ()
  860.   (declare (object-variable my-file-name))
  861.   (let* ((the-file-name (choose-file-dialog))
  862.          (the-window (with-open-file
  863.                        (the-stream the-file-name :direction :input)
  864.                        (list-to-spread (read the-stream)))))
  865.     (ask the-window
  866.       (setq my-file-name the-file-name)
  867.       (set-window-title (mac-filename the-file-name)))))
  868.  
  869.  
  870. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  871. ;;
  872. ;;  code to set up the cosell menu.
  873. ;;
  874. (defun init-cosell-menu (&aux old-menu)
  875.   (when (setq old-menu (find-menu "cosell"))
  876.         (ask old-menu (menu-deinstall)))
  877.     (let ((men (oneof *menu* :menu-title "Cosell"))
  878.           (new
  879.            (oneof *menu-item*
  880.                   :menu-item-title "New"
  881.                   :menu-item-action #'(lambda ()
  882.                                         (oneof *cosell-window*))))
  883.           (open
  884.            (oneof *menu-item*
  885.                   :menu-item-title "Open…"
  886.                   :menu-item-action 'cosell-open))
  887.           (auto
  888.            (oneof *menu-item*
  889.                   :menu-item-title "Auto Calculate"
  890.                   :menu-item-action
  891.                   #'(lambda ()
  892.                       (ask (front-window)
  893.                         (ask spread-table
  894.                           (if auto-calc-p
  895.                             (setq auto-calc-p nil)
  896.                             (progn (setq auto-calc-p t)
  897.                                    (calculate))))))))
  898.           (calc
  899.            (oneof *menu-item*
  900.                   :menu-item-title "Calculate Now"
  901.                   :menu-item-action
  902.                   #'(lambda ()
  903.                       (ask (front-window)
  904.                         (ask spread-table (calculate))))))
  905.           (trace
  906.            (oneof *menu-item*
  907.                   :menu-item-title "Trace"
  908.                   :menu-item-action
  909.                   #'(lambda ()
  910.                       (ask (front-window)
  911.                         (ask spread-table
  912.                           (setq table-trace-p (not table-trace-p))))))))
  913.       (defobfun (menu-update men) ()
  914.         (let* ((cosell-on-top-p (typep (front-window)
  915.                                        *cosell-window*)))
  916.           (dolist (the-item (cddr (menu-items)))   ;do all but the first two
  917.             (ask the-item
  918.               (if cosell-on-top-p
  919.                 (menu-item-enable)
  920.                 (menu-item-disable)))))
  921.         (usual-menu-update))
  922.       (defobfun (menu-item-update auto) ()
  923.         (when (menu-item-enabled-p)
  924.           (set-menu-item-check-mark
  925.            (ask (front-window)
  926.              (ask spread-table auto-calc-p)))))
  927.       (defobfun (menu-item-update calc) ()
  928.         (and (menu-item-enabled-p)
  929.              (ask (front-window)
  930.                (ask spread-table auto-calc-p))
  931.              (menu-item-disable)))
  932.       (defobfun (menu-item-update trace) ()
  933.         (when (menu-item-enabled-p)
  934.           (set-menu-item-check-mark
  935.            (ask (front-window)
  936.              (ask spread-table table-trace-p)))))
  937.       (ask men
  938.         (add-menu-items new open auto calc trace)
  939.         (menu-install))))
  940.  
  941. (init-cosell-menu)